package cn.bellychang.linkedlist.wangwenjun;

import java.util.NoSuchElementException;
import java.util.Objects;

/**
 * 单向链表 one-way linked list
 * construct a stack by using addFirst and removeFirst methods
 *
 * @author: bellychang
 * @date: 2020/2/2
 */
public class LinkedList<E> {

    private final static String PLAIN_NULL = "null";

    /**
     * represent linkedlist first node
     */
    private Node<E> first;

    /**
     * represent linkedlist tail Node pointer null
     */
    private final Node<E> NULL = null;

    private int size;

    public LinkedList() {
        first = NULL;
    }

    public int getSize() {
        return size;
    }

    public boolean isEmpty() {
        return size == 0;
    }

    public void addFirst(E e) {
        final Node<E> newNode = new Node<>(e);
        newNode.next = first;
        this.size++;
        first = newNode;
    }

    public boolean contains(E e) {
        Node current = first;
        while (current != null) {
            if (current.value == e) {
                return true;
            } else {
                current = current.next;
            }
        }
        return false;
    }

    public E removeFirst() {
        if (this.isEmpty()) {
            throw new NoSuchElementException("the linked list is empty");
        }
        Node<E> temp = first;
        first = temp.next;
        this.size--;
        return temp.value;
    }

    /**
     * static factory method
     *
     * @param elements linkedlist elements
     * @param <E>      the type of elements held in this collection
     * @return linkedlist instance
     */
    public static <E> LinkedList<E> of(E... elements) {
        LinkedList<E> linkedList = new LinkedList<>();
        if (elements.length != 0) {
            for (E element : elements) {
                linkedList.addFirst(element);
            }
        }
        return linkedList;
    }

    private static class Node<E> {
        /**
         * represent Node value
         */
        E value;

        /**
         * represent Node next pointer
         */
        Node<E> next;

        public Node(E value) {
            this.value = value;
        }

        @Override
        public String toString() {
            if (!Objects.isNull(value)) {
                return value.toString();
            }
            return PLAIN_NULL;
        }
    }

    @Override
    public String toString() {
        if (this.isEmpty()) {
            return PLAIN_NULL;
        }
        StringBuilder builder = new StringBuilder();
        Node<E> temp = first;
        while (Objects.nonNull(temp)) {
            builder.append(temp.toString()).append("->");
            temp = temp.next;
        }
        String result = builder.toString();
        return result.substring(0, result.length() - 2);
    }
}
